package jaux.tank.generator.jd.components;

import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import jaux.tank.generator.jd.events.TreeNodeListener;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JdTree extends JTree {

	private static final long serialVersionUID = 1L;

	
		
	public JdTree() {
		super();
		init();
	}
	
	public JdTree(TreeModel sampleTreeModel) {
		super(sampleTreeModel);
		init();
	}

	private void init() {
		setCellRenderer(new DefaultTreeCellRenderer() {
			@Override
	        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
	            super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);

	            // 根据节点的内容设置显示文本
	            if (value instanceof DefaultMutableTreeNode) {
	            	if(value instanceof JdTreeNode) {
	            		JdTreeNode jdTreeNode = (JdTreeNode)value;
	            		setText(jdTreeNode.label);
	            	}else {
	            		
	            		DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
	            		Object userObject = node.getUserObject();
	            		if (userObject != null) {
	            			setText(userObject.toString());
	            		}
	            	}
	            }

	            return this;
	        }
		});
		this.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				super.mouseClicked(e);
				 // 获取点击位置的树节点
                int x = e.getX();
                int y = e.getY();
                int row = getRowForLocation(x, y);
                if(row == -1) {
                	return;
                }
                Object node = getPathForRow(row).getLastPathComponent();
                if(! (node instanceof JdTreeNode)) {
	                return;
                }
        		JdTreeNode clickedNode = (JdTreeNode)node;
        		log.debug("Clicked on: " + clickedNode.getLabel());
        		if(clickedNode.disabled) {
        			return;
        		}
        		if (SwingUtilities.isRightMouseButton(e)) {
        			if(clickedNode.rightClickListeners != null) {
        				clickedNode.rightClickListeners.forEach(listener -> {
	        				listener.nodeClicked(e, clickedNode);
        				});
        			}
    			}else if(SwingUtilities.isLeftMouseButton(e)) {
    				if(clickedNode.clickListeners != null) {
    					clickedNode.clickListeners.forEach(listener -> {
    						listener.nodeClicked(e, clickedNode);
    					});
    				}
    				if(e.getClickCount() == 2 && (clickedNode.doubleClickListeners != null)) {
							clickedNode.doubleClickListeners.forEach(listener -> {
								listener.nodeClicked(e, clickedNode);
							});
						
					}
    			}
			}
		});
	}
	
	
	public Map<String, Boolean> getAllNodeStates() {
		return getAllNodeStates(null, null);
	}
	
	private   Map<String, Boolean> getAllNodeStates(JdTreeNode node, TreePath path) {
        if (node == null) {
            node = (JdTreeNode) this.getModel().getRoot();
            path = new TreePath(node.getPath());
        }
        Map<String, Boolean> states = new HashMap<>();
        log.debug("store " + path + " as " + this.isExpanded(path));
        states.put(path.toString(), this.isExpanded(path));

        for (int i = 0; i < node.getChildCount(); i++) {
            JdTreeNode childNode = (JdTreeNode) node.getChildAt(i);
            TreePath childPath = path.pathByAddingChild(childNode);
            states.putAll(getAllNodeStates( childNode, childPath));
        }
        return states;
    }
	
	public void setAllNodeStates(Map<String, Boolean> treeStates) {
		log.debug("set node states: " + treeStates);
		setNodeStates(null, null, treeStates);
	}
	private void setNodeStates(JdTreeNode node, TreePath path, Map<String, Boolean> treeStates) {
		
		if (node == null) {
			node = (JdTreeNode) this.getModel().getRoot();
			path = new TreePath(node.getPath());
		}
		String pathStr = path.toString();
		log.debug("set " + pathStr + " to " + treeStates.get(pathStr));
		if(treeStates.containsKey(pathStr)) {
			this.setExpandedState(path, treeStates.get(pathStr));
		}
		for (int i = 0; i < node.getChildCount(); i++) {
			JdTreeNode childNode = (JdTreeNode) node.getChildAt(i);
			TreePath childPath = path.pathByAddingChild(childNode);
			setNodeStates( childNode, childPath, treeStates);
		}
	}
	
	@Getter
	@Setter
	public static class JdTreeNode extends DefaultMutableTreeNode{
		private static final long serialVersionUID = 1L;
		private final String id;
		private final String name;
		private String label;
		private Object value;
		private boolean disabled;
		private List<TreeNodeListener> clickListeners;
		private List<TreeNodeListener> doubleClickListeners;
		private List<TreeNodeListener> rightClickListeners;
		
		public JdTreeNode(String name) {
			this(name, name, name);
		}
		public JdTreeNode(String name, Object value) {
			this(name, name, value);
		}
		public JdTreeNode(String name, String label, Object value) {
			this(name, label, value, false);
		}
		public JdTreeNode(String name, String label, Object value, boolean disabled) {
			this(UUID.randomUUID().toString(),name, label, value, disabled);
		}
		public JdTreeNode(String id, String name, String label, Object value, boolean disabled) {
			super();
			this.id = id;
			this.name = name;
			this.label = label;
			this.value = value;
			this.disabled = disabled;
			this.setUserObject(value);
		}
		
		
		public void onClick(TreeNodeListener treeNodeListener) {
			if(clickListeners == null) {
				clickListeners = new ArrayList<>();
			}
			clickListeners.add(treeNodeListener);
			
		}
		public void onDoubleClick(TreeNodeListener treeNodeListener) {
			if(doubleClickListeners == null) {
				doubleClickListeners = new ArrayList<>();
			}
			doubleClickListeners.add(treeNodeListener);
			
		}
		public void onRightClick(TreeNodeListener treeNodeListener) {
			if(rightClickListeners == null) {
				rightClickListeners = new ArrayList<>();
			}
			rightClickListeners.add(treeNodeListener);
			
		}
		
	}
}
